package Cluedo.API;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Properties;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

/**
 * 
 * As the name gives away, SoundEngine is concerned with playing sounds in the game. 
 *
 * To use SoundEngine:<p>
 *  SoundEngine soundEngine = new SoundEngine();<br>
 *  SoundEngine.bufferSound("sound.id");<br>
 * <p>
 * A hashtable deals with the lookup between the filename and the
 * sound id. For instance:<br>
 * "AI.Win" == "loud_win.wav"<br>
 * "Player.Win" == "quiet_win.wav"<p>
 * Etc.
 * 
 *  
 * @author Alex
 *
 */
public class SoundEngine extends Thread {
	
	/**
	 * List of sounds waiting to be played.
	 */
	private static ArrayList sounds = new ArrayList();
	/**
	 *  Can be used to close down the SoundEngine.
	 */
	static boolean shutdownEngine = false;
	
	/**
	 * Lookup table of sound event names to sound files.
	 */
	private static Properties soundFiles = new Properties();

	
	static String defaultSoundDirectory = "Resources/Sounds/";
	static String mappingsFile = "Resources/Config/sounds.mapping";

	
	static final int debugLevel = 0;
	
	static boolean playingEnabled = true;
	
	
	public static boolean threadAlive = false;
	
	/**
	 * Creates a soundengine thread to process queued sounds
	 *  
	 */
	public SoundEngine() {
		
	try {
            soundFiles.load(new FileInputStream(mappingsFile));
        }
        catch(Exception e) {}
        System.out.println("Sounds: "+ soundFiles.size());
	}
	
	/**
	 * Returns an array of keys for all possible sounds.
	 * 
	 * @return an array of bound sounds
	 */
	public static Properties getBindings() {
        return soundFiles;
	}
	
	
	public void run() {
	    threadAlive = true;
	    
		try {
			while(shutdownEngine == false) {
				Thread.sleep(100);
				while(sounds.size() > 0) {
				    String nextSound = (String)sounds.get(0);
			        playSound(defaultSoundDirectory + nextSound);

			        sounds.remove(0);
					if(debugLevel > 1)
						System.out.println("[SoundEngine] " + this);
				}
			}
			
		}
		catch(Exception e) {
			System.out.println("[SoundEngine] quitting");
		}
	}
	
	public static void shutdownEngine() {
		shutdownEngine = true;
	}
	
	/**
	 * Queues a sound to be played
	 * 
	 * @param sound the sound.id, not the file name
	 */
	public static void bufferSound(String sound) {
		System.out.println("Play : " + sound + " = " + soundFiles.getProperty(sound));

		if(playingEnabled == false)
			return;

		
		if(sounds.size() < 4) {
			sounds.add(soundFiles.getProperty(sound));
		}
	}
	
	synchronized public static void playSound(String fileName) {
		try {
			// From file
			System.out.println("File: "+ (new URL(fileName)) );
			
		}
		catch(Exception e )
		{}
		
		try {
			
			
			// From URL
			AudioInputStream stream = AudioSystem.getAudioInputStream( (new File(fileName).toURL())  );
			
			// At present, ALAW and ULAW encodings must be converted
			// to PCM_SIGNED before it can be played
			AudioFormat format = stream.getFormat();
			if (format.getEncoding() != AudioFormat.Encoding.PCM_SIGNED) {
				format = new AudioFormat(
						AudioFormat.Encoding.PCM_SIGNED,
						format.getSampleRate(),
						format.getSampleSizeInBits()*2,
						format.getChannels(),
						format.getFrameSize()*2,
						format.getFrameRate(),
						true);        // big endian
				stream = AudioSystem.getAudioInputStream(format, stream);
			}
			
			// Create the clip
			DataLine.Info info = new DataLine.Info(
					Clip.class, stream.getFormat(), ((int)stream.getFrameLength()*format.getFrameSize()));
			Clip clip = (Clip) AudioSystem.getLine(info);
			
			// This method does not return until the audio file is completely loaded
			clip.open(stream);
			
			// Start playing
			clip.start();
			clip = null;
			
		} catch (MalformedURLException e) {
			System.out.println("Badly formed URL");
		} catch (IOException e) {
			System.out.println("Can't open file\n" + e);
		} catch (LineUnavailableException e) {
			System.out.println("Can't get line");
		} catch (UnsupportedAudioFileException e) {
			System.out.println("Unsupported file");
		}

		
	}
	
	public static void setPlaybackStatus(boolean value) {
		playingEnabled = value;
	}
    /**
     * @return Returns if a sound exists.
     */
    public static boolean soundExists(String key) {
        String exists = soundFiles.getProperty(key);
        return new File(defaultSoundDirectory + exists).exists();
    }
}
